/*
 * ============================================================================
 * GNU General Public License
 * ============================================================================
 *
 * Copyright (C) 2015 Infinite Automation Software. All rights reserved.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * When signing a commercial license with Infinite Automation Software,
 * the following extension to GPL is made. A special exception to the GPL is
 * included to allow you to distribute a combined work that includes BAcnet4J
 * without being obliged to provide the source code for any proprietary components.
 *
 * See www.infiniteautomation.com for commercial license options.
 *
 * @author Matthew Lohbihler
 */
package org.thanos.bacnet.util;

import org.thanos.bacnet.LocalDevice;
import org.thanos.bacnet.RemoteDevice;
import org.thanos.bacnet.exception.BACnetException;
import org.thanos.bacnet.service.acknowledgement.ReadPropertyAck;
import org.thanos.bacnet.service.confirmed.ReadPropertyRequest;
import org.thanos.bacnet.type.Encodable;
import org.thanos.bacnet.type.enumerated.PropertyIdentifier;
import org.thanos.bacnet.type.primitive.ObjectIdentifier;

public class DiscoveryUtils {

	/**
	 * 获取设备扩展信息
	 *
	 * @param localDevice
	 * @param d
	 * @param flag        为直连功能使用
	 * @throws BACnetException
	 */
	public static void getExtendedDeviceInformation(final LocalDevice localDevice, final RemoteDevice d, final boolean flag)
			throws BACnetException {
		final ObjectIdentifier oid = d.getObjectIdentifier();

		// Get the device's supported services
		if (d.getServicesSupported() == null) {
			final ReadPropertyAck supportedServicesAck = localDevice
					.send(d, new ReadPropertyRequest(oid, PropertyIdentifier.protocolServicesSupported)).get();
			d.setDeviceProperty(PropertyIdentifier.protocolServicesSupported, supportedServicesAck.getValue());
		}

		// Uses the readProperties method here because this list will probably be extended.
		final PropertyReferences properties = new PropertyReferences();
		addIfMissing(d, properties, PropertyIdentifier.objectName);
		addIfMissing(d, properties, PropertyIdentifier.protocolVersion);
		addIfMissing(d, properties, PropertyIdentifier.vendorIdentifier);
		addIfMissing(d, properties, PropertyIdentifier.modelName);
		addIfMissing(d, properties, PropertyIdentifier.maxSegmentsAccepted);
		if (flag) {
			add(d, properties, PropertyIdentifier.maxApduLengthAccepted);
			add(d, properties, PropertyIdentifier.segmentationSupported);
		}

		if (properties.size() > 0) {
			// Only send a request if we have to.
			final PropertyValues values = RequestUtils.readProperties(localDevice, d, properties, true, null);

			values.forEach((opr) -> {
				final Encodable value = values.getNullOnError(oid, opr.getPropertyIdentifier());
				d.setDeviceProperty(opr.getPropertyIdentifier(), value);
			});
		}
	}

	private static void addIfMissing(final RemoteDevice d, final PropertyReferences properties,
	                                 final PropertyIdentifier pid) {
		if (d.getDeviceProperty(pid) == null)
			properties.add(d.getObjectIdentifier(), pid);
	}

	private static void add(final RemoteDevice d, final PropertyReferences properties,
	                        final PropertyIdentifier pid) {
		properties.add(d.getObjectIdentifier(), pid);
	}
}
